# --------------------------------------------------------------------------------------------------------------------
# Project:             Digital Payment System (DPS)
# Website:             http://code.google.com/p/dps-x509/
# Purpose of document: Web service test tool for registrar
# --------------------------------------------------------------------------------------------------------------------
import urllib, sys

sys.path.append("..")

try:
  import simplejson as json
except ImportError:
  import json
  
import dps.utils
import dps.x509Helper

import testCertificateData
  
HOST = ""
SPEW = False


# --------------------------------------------------------------------------------------------------------------------
def append(l, s):
  """ append l to s and return l. will also print if SPEW == True """
  l.append(s)
  
  global SPEW
  if SPEW:
    print(s)
  return l

# --------------------------------------------------------------------------------------------------------------------
def callWebService(name, params, exp):
  """ call the web service and compare expected and actual results
      @param name   - name of the web service to call
      @param params - struct of input parameters
      @param exp    - json str formatted expected results
      @return success, message tuple. success = true if expected result = actual result """
  #print ("executing "+name+" ...");
  res = False
  message = ""
  try:
    global HOST
    #print len(params)
    p = urllib.urlencode(params)
    #print params    
    f = urllib.urlopen(HOST + name, p)
    actual = urllib.unquote(f.read())
    f.close()
    if actual == exp:
      res = True
    else:
      message = "failed. actual len: "+str(len(actual))+" expected:"+str(len(exp))+"\n"
      message += "actual: \n"+actual+"\n expected: \n"+exp
  except:
    message = "Error connecting to web service"
  return res, message    
  
# --------------------------------------------------------------------------------------------------------------------
class TestSuite:
  """ container holding all tests """
  def __init__(self):
    self.tests_ = []
    
  def addTest(self, t):
    """ add a test to the suite. 
    @param t Test must be a bool,string returning function that takes no params """
    self.tests_.append(t)
    
  def runTests(self):
    """ run all tests in suite """
    passed = 0    
    o = []
    o = append(o, "********************************")
    for t in self.tests_:
      msg = ""      
      didPass = False
      resText = "False"
      try:
        didPass, msg = t()
        if didPass:
          passed += 1
          resText = "True"
      except Exception, e:
        msg = "An exception occured: " + str(sys.exc_info()[0]) + " " + str(e)
      o = append(o, "**** %s: %s" % (t.__name__, resText))
      if not didPass:
        msg = " "*4 + msg.replace("\n", "\n"+" "*4)
        o = append(o, msg)
    o = append(o, "********************************")
    o = append(o, "**** Tests passed: %d of %d" % (passed, len(self.tests_)))
    o = append(o, "********************************")
    return o

# --------------------------------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------------------------------
def clearCertificate():
  result = json.dumps({"returnCode": "True"})  
  isMatch, msg = callWebService("registrar/clear", {"format": "json"}, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
def base64_2048_lengthPublicKeyGood():
  params = {"certificate":testCertificateData.testbase64_crt(), "format": "json"}
  result = json.dumps({"returnCode": "Valid"})
  
  isMatch, msg = callWebService("registrar/upload", params, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
def certificateAlreadyInRegistrar():
  isOk, msg = clearCertificate()
  if not isOk:
    return False, "Could not clear registry"
  
  isOk, msg = base64_2048_lengthPublicKeyGood()
  if not isOk:
    return False, "Could not add first certificate"
    
  params = {"certificate":testCertificateData.testbase64_crt(), "format": "json"}
  result = json.dumps({"returnCode": "Rejected:CertificateAlreadyInRegistrarError"})
  
  isMatch, msg = callWebService("registrar/upload", params, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
def base64_1024_lengthPublicKeyGood():
  params = {"certificate":testCertificateData.testbase64_1024pubkey_crt(), "format": "json"}
  result = json.dumps({"returnCode": "Valid"})
  
  isMatch, msg = callWebService("registrar/upload", params, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
def base64dsa():
  params = {"certificate":testCertificateData.testbase64dsa_crt(), "format": "json"}
  result = json.dumps({"returnCode": "Rejected:NoRSADataFoundError"})
  
  isMatch, msg = callWebService("registrar/upload", params, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
def base64BadSignature():
  params = {"certificate":testCertificateData.testbase64switchchars_crt(), "format": "json"}
  result = json.dumps({"returnCode": "Rejected:InvalidSignatureError"})
  
  isMatch, msg = callWebService("registrar/upload", params, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
def base64ExtraCharacters():
  params = {"certificate":testCertificateData.testbase64extra_crt(), "format": "json"}
  result = json.dumps({"returnCode": "Rejected:DecodingError"})
  
  certChain = dps.x509Helper.certsToBase64(testCertificateData.testbase64extra_crt())
  dps.utils.assertion(len(certChain) == 1, "1 certificate expected")
  
  isMatch, msg = callWebService("registrar/upload", params, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
def derGood():
  isOk, msg = clearCertificate()
  if not isOk:
    return False, "Could not clear registry"

  cert = open("../certificates/testder.crt", "rb").read()  
  certChain = dps.x509Helper.certsToBase64(cert)

  params = {"certificate":certChain[0], "format": "json"}
  result = json.dumps({"returnCode": "Valid"})
  
  isOk, msg = callWebService("registrar/upload", params, result);
  return isOk, msg
  
# --------------------------------------------------------------------------------------------------------------------
def nonSelfSignedWithChain():
  params = {"certificate":testCertificateData.mobileMinutesWithChain_crt(), "format": "json"}
  result = json.dumps({"returnCode": "Valid"})
  
  isMatch, msg = callWebService("registrar/upload", params, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
def nonSelfSignedWithOutChain():
  params = {"certificate":testCertificateData.secondRecipientSingleCertificate_crt(), "format": "json"}
  result = json.dumps({"returnCode": "Valid"})
  
  isMatch, msg = callWebService("registrar/upload", params, result);
  return isMatch, msg

# --------------------------------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------------------------------
def runTests(host = "http://dps-x509.appspot.com/", spewOutput = False):
  """ execute the tests in the test suite. the default params are configured for the web gui interfaces
  @param host - where to run the app
  @param spewOutput - print output as the tests are running. This is useful when running locally, but isn't wanted when running via the gui web interface. 
  @return a list of string results. """
  global HOST
  global SPEW
  
  HOST = host
  SPEW = spewOutput
    
  out = []
  if SPEW:
    append(out, "host: "+HOST)  
  ts = TestSuite()

  ts.addTest(clearCertificate)
  ts.addTest(base64_2048_lengthPublicKeyGood)
  ts.addTest(base64_1024_lengthPublicKeyGood)
  ts.addTest(certificateAlreadyInRegistrar)
  ts.addTest(base64dsa)
  ts.addTest(base64BadSignature)
  ts.addTest(base64ExtraCharacters)
  ts.addTest(derGood) 
  ts.addTest(nonSelfSignedWithChain)
  ts.addTest(nonSelfSignedWithOutChain)

  out += ts.runTests()
  
  return out
  
# --------------------------------------------------------------------------------------------------------------------
if __name__ == "__main__":
  runTests("http://dps-x509.appspot.com/", True)
  #runTests("http://localhost:8083/", True)

